Skip to main content

5.3 - Worker Commands- Gather and Build

Worker units are the engine of your economy and the architects of your base. They have two primary, specialized commands: gather for harvesting resources and build for constructing structures. While gathering is a simple "fire-and-forget" command, building requires a more deliberate, multi-step process.


The gather Command: Your Economic Engine

The gather command is the simplest and most fundamental worker action.

  • Signature: worker.gather(target)
  • Target: A Unit object that is a MineralField or VespeneGeyser.
  • Behavior: The worker will travel to the resource, harvest it, return it to the nearest townhall, and continue this loop indefinitely. You only need to issue this command once per worker.
# A common pattern: find idle workers and put them to work.
for worker in self.workers.idle:
# Find the closest mineral patch to this specific worker.
closest_mineral_patch = self.mineral_field.closest_to(worker)
# Issue the gather command.
self.do(worker.gather(closest_mineral_patch))

The Build Request Lifecycle

Constructing a building is not a single command but a workflow. You must first ask the game for a valid location before you can command a worker to build there.

(You decide to build a Pylon)
|
v
.---- CAN I BUILD? -------------.
| - self.can_afford() |
| - self.already_pending() == 0 |
`-------------------------------`
| (Yes)
v
.---- WHERE TO BUILD? -------------------.
| location = await self.find_placement() |
`----------------------------------------`
| (Location is valid)
v
.---- ISSUE COMMAND ----------------.
| await self.build(PYLON, location) |
`-----------------------------------`
|
v
(Worker begins construction)

This lifecycle prevents errors and ensures your bot doesn't try to build in illegal locations or waste resources on redundant structures.


Key Functions for Building

FunctionSignatureRole in the Lifecycle
self.can_afford(unit_id)(UnitTypeId) -> boolCondition Check. Returns True if you have enough minerals and gas for a given unit or structure.
self.already_pending(unit_id)(UnitTypeId) -> int or floatCondition Check. Returns the number of a given unit/structure type currently in production. Essential for avoiding duplicates.
self.find_placement(...)(UnitTypeId, near, ...) -> Point2 or NoneLocation Finding. Asks the game for a valid build spot. This is an async function and must be awaited.
self.build(...)(UnitTypeId, near_or_location) -> UnitCommandAction Issuing. The high-level helper that finds a worker and issues the final build command. This is also async.

Deep Dive: self.find_placement()

This is a powerful but potentially slow function. Understanding its parameters is key to using it effectively.

ParameterTypeDescription
buildingUnitTypeIdRequired. The specific building you want to place (e.g., UnitTypeId.PYLON).
nearPoint2Required. The center point of the search area.
max_distanceintThe maximum radius to search from the near point. Defaults to 20.
placement_stepintThe grid size of the search. A higher value (e.g., 5) is faster but less precise. A lower value (e.g., 1) is slower but will find tighter spots. Defaults to 2.

Code Example: The Architect Bot

This bot demonstrates a robust and scalable building system. Instead of reacting instantly, it uses a simple list as a "build queue" to manage its construction projects, tackling one at a time.

# architect_bot.py

from collections import deque
from sc2 import maps
from sc2.bot_ai import BotAI
from sc2.data import Difficulty, Race
from sc2.main import run_game
from sc2.player import Bot, Computer
from sc2.ids.unit_typeid import UnitTypeId

class ArchitectBot(BotAI):
"""A bot that uses a build queue to manage construction projects."""
def __init__(self):
super().__init__()
# Use a deque as a simple, efficient queue.
self.build_queue = deque()

async def on_step(self, iteration: int):
await self.distribute_workers()
self.update_build_queue()
await self.execute_build_queue()

def update_build_queue(self):
"""Adds required buildings to the queue."""
# Queue a Supply Depot if we are running low on supply.
if (
self.supply_left < 5
and self.already_pending(UnitTypeId.SUPPLYDEPOT) == 0
and UnitTypeId.SUPPLYDEPOT not in self.build_queue
):
self.build_queue.append(UnitTypeId.SUPPLYDEPOT)

# Queue a Barracks if we have a depot but no barracks.
if (
self.structures(UnitTypeId.SUPPLYDEPOT).ready.exists
and not self.structures(UnitTypeId.BARRACKS).exists
and self.already_pending(UnitTypeId.BARRACKS) == 0
and UnitTypeId.BARRACKS not in self.build_queue
):
self.build_queue.append(UnitTypeId.BARRACKS)

async def execute_build_queue(self):
"""Works on the first item in the build queue."""
if not self.build_queue:
return

# Get the next building project from the front of the queue.
building_to_construct = self.build_queue[0]

# Check if we can afford the project.
if self.can_afford(building_to_construct):
# Find a location for the building.
build_location = await self.find_placement(
building_to_construct, near=self.start_location, placement_step=5
)

if build_location:
# Issue the build command.
await self.build(building_to_construct, build_location)
# Remove the completed project from the queue.
self.build_queue.popleft()


if __name__ == "__main__":
run_game(
maps.get("BlackburnAIE"),
[
Bot(Race.Terran, ArchitectBot()),
Computer(Race.Zerg, Difficulty.VeryEasy)
],
realtime=True,
)